#pragma once
#include "headers.h"
#include "Module.h"

namespace Engine
{
#ifdef _WIN32
  // variadic templates Variant

  template<typename Ret, typename... Args >
  class ModuleDelegate;

  template<typename Ret, typename... Args >
  class ModuleDelegate<Ret (Args...)>
    {
    public:
      Module *sender;
      typedef function<Ret (Args...)> funcType;
      typedef  map<Module*, funcType > CallbackList;
      typedef pair<Module*, funcType > CallbackItem;
      CallbackList    callbackList;
    public:
      typedef  map<Module*, Ret>       CallbackResults;
      typedef pair<Module*, Ret>       CallbackResult;
    protected:
      CallbackResults callbackResults;
    public:
      ModuleDelegate()
        {
        sender = nullptr;
        }
      ModuleDelegate(Module *sender)
        {
        sender = sender;
        }
      void print()
        {
        cout << "args_count" << sizeof...(Args);
        }
      bool hasListener(Module *listener)
        {
        if(callbackList.find(listener) != callbackList.end())
          return true;
        return false;
        }
      ModuleDelegate& addListener(Module *listener, funcType callback)
        {
        if(!hasListener(listener))
          callbackList.insert(CallbackItem(listener, callback));
        return this;
        }
      ModuleDelegate& removeListener(Module *listener)
        {
        if(hasListener(listener))
          callbackList.erase(listener);
        return this;
        }
      ModuleDelegate& removeAllListeners()
        {
        for(auto it = callbackList.begin(); it!=callbackList.end(); ++it)
          removeListener(it->first);
        return this;
        }
      CallbackResults invoke(Args... args)
        {
        //Ret last_result;
        for(auto it = callbackList.begin(); it!=callbackList.end(); ++it)
          {
          Ret last_result = it->second(args...);
          callbackResults.insert(CallbackResult(it->first, last_result));
          }
        return callbackResults;
        }
      CallbackResults operator()(Args... args)
        {
        return invoke(args...);
        }
      Ret getResult(Module *listener)
        {
        }
    };


  // Support - Alias
  /*template<typename Ret, typename... Args >
  class ModuleDelegate0: public ModuleDelegate<Ret, Args...>
    {
    public:
      ModuleDelegate0():ModuleDelegate<Ret, Args...>()
        {
        }
      ModuleDelegate0(Module *sender):ModuleDelegate<Ret, Args...>(sender)
        {
        }
    };
  template<typename Ret, typename... Args >
  class ModuleDelegate1: public ModuleDelegate<Ret, Args...>
    {
    public:
      ModuleDelegate1():ModuleDelegate<Ret, Args...>()
        {
        }
      ModuleDelegate1(Module *sender):ModuleDelegate<Ret, Args...>(sender)
        {
        }
    };
  template<typename Ret, typename... Args >
  class ModuleDelegate2: public ModuleDelegate<Ret, Args...>
    {
    public:
      ModuleDelegate2():ModuleDelegate<Ret, Args...>()
        {
        }
      ModuleDelegate2(Module *sender):ModuleDelegate<Ret, Args...>(sender)
        {
        }
    };
  template<typename Ret, typename... Args >
  class ModuleDelegate3: public ModuleDelegate<Ret, Args...>
    {
    public:
      ModuleDelegate3():ModuleDelegate<Ret, Args...>()
        {
        }
      ModuleDelegate3(Module *sender):ModuleDelegate<Ret, Args...>(sender)
        {
        }
    };
  template<typename Ret, typename... Args >
  class ModuleDelegate4: public ModuleDelegate<Ret, Args...>
    {
    public:
      ModuleDelegate4():ModuleDelegate<Ret, Args...>()
        {
        }
      ModuleDelegate4(Module *sender):ModuleDelegate<Ret, Args...>(sender)
        {
        }
    };*/
#else
  // NON Variadic templates variant

  /*template<>
  class ModuleDelegate
    {

    };*/

  template<typename Ret>
  class ModuleDelegate0
    {
    private:
      Module *sender;
      typedef function<Ret ()> funcType;
      typedef  map<Module*, funcType > CallbackList;
      typedef pair<Module*, funcType > CallbackItem;
      CallbackList    callbackList;
    public:
      typedef  map<Module*, Ret>       CallbackResults;
      typedef pair<Module*, Ret>       CallbackResult;
    private:
      CallbackResults callbackResults;
    public:
      ModuleDelegate0()
        {
        sender = nullptr;
        }
      ModuleDelegate0(Module *sender)
        {
        sender = sender;
        }
      bool hasListener(Module *listener)
        {
        if(callbackList.find(listener) != callbackList.end())
          return true;
        return false;
        }
      ModuleDelegate0& addListener(Module *listener, funcType callback)
        {
        if(!hasListener(listener))
          callbackList.insert(CallbackItem(listener, callback));
        return this;
        }
      ModuleDelegate0& removeListener(Module *listener)
        {
        if(hasListener(listener))
          callbackList.erase(listener);
        return this;
        }
      ModuleDelegate0& removeAllListeners()
        {
        for(auto it = callbackList.begin(); it!=callbackList.end(); ++it)
          removeListener(it->first);
        return this;
        }
      CallbackResults invoke()
        {
        //Ret last_result;
        for(auto it = callbackList.begin(); it!=callbackList.end(); ++it)
          {
          Ret last_result = it->second();
          callbackResults.insert(CallbackResult(it->first, last_result));
          }
        return callbackResults;
        }
      CallbackResults operator()()
        {
        return invoke();
        }
      Ret getResult(Module *listener)
        {
        }
    };
  template<typename Ret, typename Arg1>
  class ModuleDelegate1
    {
    private:
      Module *sender;
      typedef function<Ret (Arg1)> funcType;
      typedef  map<Module*, funcType > CallbackList;
      typedef pair<Module*, funcType > CallbackItem;
      CallbackList    callbackList;
    public:
      typedef  map<Module*, Ret>       CallbackResults;
      typedef pair<Module*, Ret>       CallbackResult;
    private:
      CallbackResults callbackResults;
    public:
      ModuleDelegate1()
        {
        sender = nullptr;
        }
      ModuleDelegate1(Module *sender)
        {
        sender = sender;
        }
      bool hasListener(Module *listener)
        {
        if(callbackList.find(listener) != callbackList.end())
          return true;
        return false;
        }
      ModuleDelegate1& addListener(Module *listener, funcType callback)
        {
        if(!hasListener(listener))
          callbackList.insert(CallbackItem(listener, callback));
        //return this;
        }
      ModuleDelegate1& removeListener(Module *listener)
        {
        if(hasListener(listener))
          callbackList.erase(listener);
        return this;
        }
      ModuleDelegate1& removeAllListeners()
        {
        for(auto it = callbackList.begin(); it!=callbackList.end(); ++it)
          removeListener(it->first);
        return this;
        }
      CallbackResults invoke(Arg1 arg1)
        {
        for(auto it = callbackList.begin(); it!=callbackList.end(); ++it)
          {
          Ret last_result = it->second(arg1);
          callbackResults.insert(CallbackResult(it->first, last_result));
          }
        return callbackResults;
        }
      CallbackResults operator()(Arg1 arg1)
        {
        return invoke(arg1);
        }
      Ret getResult(Module *listener)
        {
        }
    };
  template<typename Ret, typename Arg1, typename Arg2>
  class ModuleDelegate2
    {
    private:
      Module *sender;
      typedef function<Ret (Arg1,Arg2)> funcType;
      typedef  map<Module*, funcType > CallbackList;
      typedef pair<Module*, funcType > CallbackItem;
      CallbackList    callbackList;
    public:
      typedef  map<Module*, Ret>       CallbackResults;
      typedef pair<Module*, Ret>       CallbackResult;
    private:
      CallbackResults callbackResults;
    public:
      ModuleDelegate2()
        {
        sender = nullptr;
        }
      ModuleDelegate2(Module *sender)
        {
        sender = sender;
        }
      bool hasListener(Module *listener)
        {
        if(callbackList.find(listener) != callbackList.end())
          return true;
        return false;
        }
      ModuleDelegate2& addListener(Module *listener, funcType callback)
        {
        if(!hasListener(listener))
          callbackList.insert(CallbackItem(listener, callback));
        return this;
        }
      ModuleDelegate2& removeListener(Module *listener)
        {
        if(hasListener(listener))
          callbackList.erase(listener);
        return this;
        }
      ModuleDelegate2& removeAllListeners()
        {
        for(auto it = callbackList.begin(); it!=callbackList.end(); ++it)
          removeListener(it->first);
        return this;
        }
      CallbackResults invoke(Arg1 arg1, Arg2 arg2)
        {
        for(auto it = callbackList.begin(); it!=callbackList.end(); ++it)
          {
          Ret last_result = it->second(arg1, arg2);
          callbackResults.insert(CallbackResult(it->first, last_result));
          }
        return callbackResults;
        }
      CallbackResults operator()(Arg1 arg1, Arg2 arg2)
        {
        return invoke(arg1, arg2);
        }
      Ret getResult(Module *listener)
        {
        }
    };
  template<typename Ret, typename Arg1, typename Arg2, typename Arg3>
  class ModuleDelegate3
    {
    private:
      Module *sender;
      typedef function<Ret (Arg1,Arg2,Arg3)> funcType;
      typedef  map<Module*, funcType > CallbackList;
      typedef pair<Module*, funcType > CallbackItem;
      CallbackList    callbackList;
    public:
      typedef  map<Module*, Ret>       CallbackResults;
      typedef pair<Module*, Ret>       CallbackResult;
    private:
      CallbackResults callbackResults;
    public:
      ModuleDelegate3()
        {
        sender = nullptr;
        }
      ModuleDelegate3(Module *sender)
        {
        sender = sender;
        }
      bool hasListener(Module *listener)
        {
        if(callbackList.find(listener) != callbackList.end())
          return true;
        return false;
        }
      ModuleDelegate3& addListener(Module *listener, funcType callback)
        {
        if(!hasListener(listener))
          callbackList.insert(CallbackItem(listener, callback));
        return this;
        }
      ModuleDelegate3& removeListener(Module *listener)
        {
        if(hasListener(listener))
          callbackList.erase(listener);
        return this;
        }
      ModuleDelegate3& removeAllListeners()
        {
        for(auto it = callbackList.begin(); it!=callbackList.end(); ++it)
          removeListener(it->first);
        return this;
        }
      CallbackResults invoke(Arg1 arg1, Arg2 arg2, Arg3 arg3)
        {
        for(auto it = callbackList.begin(); it!=callbackList.end(); ++it)
          {
          Ret last_result = it->second(arg1, arg2, arg3);
          callbackResults.insert(CallbackResult(it->first, last_result));
          }
        return callbackResults;
        }
      CallbackResults operator()(Arg1 arg1, Arg2 arg2, Arg3 arg3)
        {
        return invoke(arg1, arg2, arg3);
        }
      Ret getResult(Module *listener)
        {
        }
    };
  template<typename Ret, typename Arg1, typename Arg2, typename Arg3, typename Arg4>
  class ModuleDelegate4
    {
    private:
      Module *sender;
      typedef function<Ret (Arg1,Arg2,Arg3,Arg4)> funcType;
      typedef  map<Module*, funcType > CallbackList;
      typedef pair<Module*, funcType > CallbackItem;
      CallbackList    callbackList;
    public:
      typedef  map<Module*, Ret>       CallbackResults;
      typedef pair<Module*, Ret>       CallbackResult;
    private:
      CallbackResults callbackResults;
    public:
      ModuleDelegate4()
        {
        sender = nullptr;
        }
      ModuleDelegate4(Module *sender)
        {
        sender = sender;
        }
      bool hasListener(Module *listener)
        {
        if(callbackList.find(listener) != callbackList.end())
          return true;
        return false;
        }
      ModuleDelegate4& addListener(Module *listener, funcType callback)
        {
        if(!hasListener(listener))
          callbackList.insert(CallbackItem(listener, callback));
        //return this;
        }
      ModuleDelegate4& removeListener(Module *listener)
        {
        if(hasListener(listener))
          callbackList.erase(listener);
        return this;
        }
      ModuleDelegate4& removeAllListeners()
        {
        for(auto it = callbackList.begin(); it!=callbackList.end(); ++it)
          removeListener(it->first);
        return this;
        }
      CallbackResults invoke(Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4)
        {
        for(auto it = callbackList.begin(); it!=callbackList.end(); ++it)
          {
          Ret last_result = it->second(arg1, arg2, arg3, arg4);
          callbackResults.insert(CallbackResult(it->first, last_result));
          }
        return callbackResults;
        }
      CallbackResults operator()(Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4)
        {
        return invoke(arg1, arg2, arg3, arg4);
        }
      Ret getResult(Module *listener)
        {
        }
    };
#endif
}
